#pragma once
#include <math.h>
#include <time.h>
#include "btBulletDynamicsCommon.h"

#define _NOTPROFILING
#ifdef _NOTPROFILING
#define _BT_PROFILE(name) 0
#else
#define _BT_PROFILE(name) BT_PROFILE(name)
#endif

class Vector3 {

private:
	float entry[3];

public:
	Vector3() { entry[0]=0.0; entry[1]=0.0f; entry[2]=0.0f;};
	Vector3(const Vector3& v) {
		for (int i=0; i<3; i++) 
			entry[i]=v.entry[i];
	};
	Vector3(const btVector3& v) {
		for (int i=0; i<3; i++) 
			entry[i]=v[i];
	};
	Vector3(float x, float y, float z) { entry[0]=x; entry[1]=y; entry[2]=z;};
	~Vector3() {};

	inline const float& x() const { return entry[0]; };
	inline const float& y() const { return entry[1]; };
	inline const float& z() const { return entry[2]; };

	inline float& x() { return entry[0]; };
	inline float& y() { return entry[1]; };
	inline float& z() { return entry[2]; };

	inline const float length() const {
		_BT_PROFILE("Vector3::length");
		return sqrt(length_square());
	}
	inline const float length_square() const {
		_BT_PROFILE("Vector3::length_square");
		return x()*x() + y()*y() + z()*z();
	}
	inline const Vector3 normalized() {
		return *this/length();
	}
	inline const Vector3& normalize() {
		return (*this/=length());
	}

#define _VECTOR3_OPERATOR
#ifdef _VECTOR3_OPERATOR

	const float operator[] (int index) const {
		return entry[index];
	};
	float &operator[] (int index) {
		return entry[index];
	};

	Vector3& operator = (const btVector3& v) {
		_BT_PROFILE("Vector3::operator=");
		entry[0]=v.x();
		entry[1]=v.y();
		entry[2]=v.z();
		return *this;
	}
	Vector3& operator = (const Vector3& v) {
		_BT_PROFILE("Vector3::operator=");
		entry[0]=v.x();
		entry[1]=v.y();
		entry[2]=v.z();
		return *this;
	}
	Vector3& operator = (const float& f) {
		_BT_PROFILE("Vector3::operator=");
		entry[0]=f;
		entry[1]=f;
		entry[2]=f;
		return *this;
	}
	Vector3 operator * (const float& f) {
		_BT_PROFILE("Vector3::operator*");
		return Vector3(entry[0] * f, entry[1] * f, entry[2] * f);
	} 
	Vector3 operator *= (const float& f) {
		_BT_PROFILE("Vector3::operator*=");
		*this = *this * f;
		return *this;
	} 
	Vector3 operator * (const int& n) {
		_BT_PROFILE("Vector3::operator*");
		return Vector3(entry[0] * n, entry[1] * n, entry[2] * n);
	}
	Vector3 operator / (const float& f) {
		_BT_PROFILE("Vector3::operator/");
		return Vector3(entry[0] / f, entry[1] / f, entry[2] / f);
	}
	Vector3& operator /= (const float& f) {
		_BT_PROFILE("Vector3::operator/=");
		*this = *this / f;
		return *this;
	}
	Vector3 operator + (const Vector3& v) {
		_BT_PROFILE("Vector3::operator+");
		return Vector3(entry[0] + v.entry[0], entry[1] + v.entry[1], entry[2] + v.entry[2]);
	}
	Vector3 operator + (const btVector3& v) {
		_BT_PROFILE("Vector3::operator+");
		return Vector3(entry[0] + v[0], entry[1] + v[1], entry[2] + v[2]);
	}
	Vector3 operator - (const Vector3& v) {
		_BT_PROFILE("Vector3::operator-");
		return Vector3(entry[0] - v.entry[0], entry[1] - v.entry[1], entry[2] - v.entry[2]);
	}
	Vector3 operator - (const btVector3& v) {
		_BT_PROFILE("Vector3::operator-");
		return Vector3(entry[0] - v[0], entry[1] - v[1], entry[2] - v[2]);
	}
	Vector3 operator * (const Vector3& v) {		// not Dot Product (x1*x2, y1*y2, z1*z2)
		_BT_PROFILE("Vector3::operator*(vector)");
		return Vector3(x() * v.x(), y() * v.y(), z() * v.z());
	}
	Vector3 operator * (const btVector3& v) {
		_BT_PROFILE("Vector3::operator*(vector)");
		return Vector3(x() * v.x(), y() * v.y(), z() * v.z());
	}
	Vector3 operator / (const Vector3& v) {
		_BT_PROFILE("Vector3::operator/(vector)");
		return Vector3(x() / v.x(), y() / v.y(), z() / v.z());
	}
	Vector3& operator += (const Vector3& v) {
		_BT_PROFILE("Vector3::operator+=");
		*this = *this + v;
		return *this;
	}
	Vector3& operator -= (const Vector3& v) {
		_BT_PROFILE("Vector3::operator-=");
		*this = *this - v;
		return *this;
	}
	Vector3& operator *= (const Vector3& v) {
		_BT_PROFILE("Vector3::operator*=");
		*this = *this * v;
		return *this;
	}

	bool operator < (const Vector3& v) {
		_BT_PROFILE("Vector3::operator<");
		return (entry[0] < v.entry[0] && entry[1] < v.entry[1] && entry[2] < v.entry[2]);
	};
	bool operator > (const Vector3& v) {
		_BT_PROFILE("Vector3::operator>");
		return (entry[0] > v.entry[0] && entry[1] > v.entry[1] && entry[2] > v.entry[2]);
	};

#endif

	const float dot(const Vector3& v1) const {
		return entry[0] * v1[0] + entry[1] * v1[1] + entry[2] * v1[2];
	};
	static const float dot(const Vector3& v1, const Vector3& v2)  {
		return v2[0] * v1[0] + v2[1] * v1[1] + v2[2] * v1[2];
	};

	operator btVector3() {
		return btVector3(x(), y(), z());
	}
	operator btVector3() const {
		return btVector3(x(), y(), z());
	}

	static Vector3 RandomVector(const float min, const float max) {
		return Vector3(min + (float)rand() / (float)RAND_MAX * (max-min), min + (float)rand() / (float)RAND_MAX * (max-min), min + (float)rand() / (float)RAND_MAX * (max-min));
	}

	void limitLength(const float max) {
		float f = max * max;
		if (length_square() > f) {
			normalize();
			*this *= max;
		}
	};
};
